package test;

/* Released under GPL2, (C) 2010 by folkert@vanheusden.com */
import java.awt.Image;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import javax.imageio.ImageIO;

/**
 * Class ReadMJPEGUrl opens a socket to a device serving an MJPEG-stream and then
 * returns each frame as an Image object.
 *
 * @author	Folkert van Heusden
 * @version	%I%, %G%
 * @since	0.1 
 */
class ReadMJPEGUrl {
	final HttpURLConnection connection;
	final InputStream input;
	final int maxCompressedFrameSize = 640 * 480;
	final int bLen;
	final String boundary;

	class MyInputStream extends InputStream {
		byte [] data;
		int pointer = 0;

		public MyInputStream(byte [] input) {
			data = input;
		}

		public int available() {
			return data.length - pointer;
		}

		public void close() {
		}

		public void mark() {
		}

		public boolean markSupported() {
			return false;
		}

		public String getEncoding() {
			return "UTF-8";
		}

		public int read() {
			if (pointer < data.length)
				return data[pointer++];

			return -1;
		}

		public int read(byte [] buf) {
			return read(buf, 0, buf.length);
		}

		public int read(byte[] buf, int offset, int length) {
			int n = Math.min(length, data.length - pointer);

			for(int index=0; index<n; index++) {
				buf[offset + index] = data[pointer++];
			}

			return n == 0 ? -1 : n;
		}

		public void reset() {
			pointer = 0;
		}

		public long skip(long n) {
			pointer += (int)n;

			return (int)n;
		}
	}

	/**
	 * Opens the connection to the server and setups the processing.
	 *
	 * @param urlStr		Url of the MJPEG stream to open.
	 */
	public ReadMJPEGUrl(String urlStr) throws Exception {

		URL url = new URL(urlStr);
		connection = (HttpURLConnection)url.openConnection();
		String cType = connection.getContentType();
		int bOffset = cType.indexOf("boundary=");
		if (bOffset == -1)
			throw new Exception("Don't know how to handle " + urlStr + ", stream not understood");
		String boundaryDummy = cType.substring(bOffset + 9);
		int bEnd = boundaryDummy.indexOf(" ");
		if (bEnd == -1)
			bEnd = boundaryDummy.indexOf(";");
		if (bEnd != -1)
			boundary = boundaryDummy.substring(0, bEnd);
		else
			boundary = boundaryDummy;
		bLen = boundary.length();

		input = connection.getInputStream();
	}

	/**
	 * Reads one frame from the MJPEG stream.
	 *
	 * @return 			Image-object
	 */
	public Image readFrame() throws Exception {
		int idLen = 0;

		byte [] imageData = new byte[maxCompressedFrameSize]; // really frames should not be that large

		byte [] crlf = new byte[4];
		do
		{
			crlf[0] = crlf[1];
			crlf[1] = crlf[2];
			crlf[2] = crlf[3];
			int c = input.read();
			if (c == -1)
				break;
			crlf[3] = (byte)c;
		}
		while(crlf[0] != '\r' || crlf[1] != '\n' || crlf[2] != '\r' || crlf[3] != '\n');

		boolean found = false;
		while(idLen < maxCompressedFrameSize) {
			int c = input.read();
			if (c == -1)
				break;
			imageData[idLen++] = (byte)c;
			if (idLen >= bLen) {
				int index = -1;
				found = false;
				for(index=bLen; index<(idLen - bLen); index++) {
					found = true;
					for(int checkLoop=0; checkLoop<bLen; checkLoop++) {
						if (imageData[index + checkLoop] != (byte)boundary.charAt(checkLoop)) {
							found = false;
							break;
						}
					}
					if (found)
						break;
				}

				if (found) {
					idLen = index;
					break;
				}
			}
		}

		if (found) {
			byte [] result = new byte[idLen];
			System.arraycopy(imageData, 0, result, 0, idLen);

			MyInputStream misr = new MyInputStream(result);

			return ImageIO.read((InputStream)misr);
		}

		return null;
	}

	/**
	 * Closes the stream.
	 */
	public void close() throws Exception {
		input.close();
		connection.disconnect();
	}
}